package cn.edu.gzgs.service;

import cn.edu.gzgs.constant.LoginStatus;
import cn.edu.gzgs.entity.LoginLog;
import cn.edu.gzgs.entity.User;
import cn.edu.gzgs.entity.UserKnownIp;
import cn.edu.gzgs.mapper.LoginLogMapper;
import cn.edu.gzgs.mapper.UserKnownIpMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.LocalDateTime;

@Service
@Slf4j
public class LoginLogService {

    private final LoginLogMapper loginLogMapper;
    private final UserKnownIpMapper userKnownIpMapper;
    private static final int MAX_FAILED_ATTEMPTS = 5; // 最大失败尝试次数
    private static final int ATTEMPT_WINDOW_MINUTES = 15; // 失败尝试的时间窗口（分钟）
    private static final int DISTINCT_IP_THRESHOLD = 3; // 不同IP数量的阈值，超过此数量的新IP被视为潜在风险

    @Autowired
    public LoginLogService(LoginLogMapper loginLogMapper, UserKnownIpMapper userKnownIpMapper) {
        this.loginLogMapper = loginLogMapper;
        this.userKnownIpMapper = userKnownIpMapper;
    }

    /**
     * 异步记录登录日志。使用 REQUIRES_NEW 传播级别确保日志记录独立于主事务。
     */
    @Async
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void recordLoginAttempt(String usernameOrUserNo, User user, LoginStatus status, String remarks) {
        HttpServletRequest request = getCurrentHttpRequest();
        String ipAddress = "Unknown";
        String userAgent = "Unknown";
        if (request != null) {
            ipAddress = getClientIpAddress(request);
            userAgent = request.getHeader("User-Agent");
        }

        LoginLog logEntry = LoginLog.builder()
                .usernameOrUserNo(usernameOrUserNo)
                .userId(user != null ? user.getId() : null)
                .ipAddress(ipAddress)
                .loginStatus(status)
                .attemptTime(LocalDateTime.now())
                .remarks(remarks)
                .userAgent(userAgent)
                .build();
        loginLogMapper.insert(logEntry);
        log.info("登录日志已记录: 用户名/工号='{}', IP='{}', 状态='{}', 备注='{}'", usernameOrUserNo, ipAddress, status, remarks);
        
        // 如果登录成功，更新或创建 UserKnownIp 记录 (在新的事务中处理，避免影响主日志记录)
        if (status == LoginStatus.SUCCESS && user != null && !"Unknown".equals(ipAddress)) {
            updateUserKnownIp(user.getId(), ipAddress);
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateUserKnownIp(Long userId, String ipAddress) {
        UserKnownIp knownIp = userKnownIpMapper.findByUserIdAndIpAddress(userId, ipAddress);
        if (knownIp != null) {
            knownIp.setLoginCount(knownIp.getLoginCount() + 1);
            // lastLoginTime 会通过 @PreUpdate 自动更新
            userKnownIpMapper.updateById(knownIp);
        } else {
            UserKnownIp newIp = UserKnownIp.builder()
                    .userId(userId)
                    .ipAddress(ipAddress)
                    .loginCount(1)
                    // firstLoginTime 和 lastLoginTime 会通过 @PrePersist 自动设置
                    .isTrusted(false) // 新IP默认为不信任
                    .build();
            userKnownIpMapper.insert(newIp);
        }
    }

    /**
     * 检查登录异常情况并记录告警
     * @param usernameOrUserNo 尝试登录的用户名或工号
     * @param user 登录成功的用户对象，如果登录失败则为null
     * @param loginStatus 当前登录尝试的状态
     */
    public String checkAndLogAbnormalLogin(String usernameOrUserNo, User user, LoginStatus loginStatus) {
        HttpServletRequest request = getCurrentHttpRequest();
        String currentIpAddress = (request != null) ? getClientIpAddress(request) : "Unknown";
        StringBuilder alertRemarks = new StringBuilder();

        // 1. 检查多次失败登录
        if (loginStatus != LoginStatus.SUCCESS) {
            LocalDateTime endTime = LocalDateTime.now();
            LocalDateTime startTime = endTime.minusMinutes(ATTEMPT_WINDOW_MINUTES);
            int failedAttempts = loginLogMapper.countFailedAttemptsInWindow(usernameOrUserNo, currentIpAddress, startTime, endTime);

            if (failedAttempts >= MAX_FAILED_ATTEMPTS -1) { // 因为当前这次失败还未记录，所以是 MAX_FAILED_ATTEMPTS - 1
                String failedAttemptMsg = String.format("警告：用户/IP [%s/%s] 在%d分钟内已连续登录失败 %d 次。",
                        usernameOrUserNo, currentIpAddress, ATTEMPT_WINDOW_MINUTES, failedAttempts + 1);
                log.warn(failedAttemptMsg);
                alertRemarks.append(failedAttemptMsg);
            }
        }

        // 2. 增强的异地登录检测 (在登录成功时)
        if (loginStatus == LoginStatus.SUCCESS && user != null && !"Unknown".equals(currentIpAddress)) {
            UserKnownIp knownIpEntry = userKnownIpMapper.findByUserIdAndIpAddress(user.getId(), currentIpAddress);
            
            if (knownIpEntry == null) { // 表示这是一个全新的IP地址对于该用户
                int distinctIpCount = userKnownIpMapper.countDistinctIpsByUserId(user.getId());
                // 这里的 distinctIpCount 是指 *之前* 已知的不同IP数量。当前这个新IP还未计入。
                if (distinctIpCount >= DISTINCT_IP_THRESHOLD) {
                     String newIpRiskMsg = String.format("风险提示：用户 [%s] 从一个全新的IP地址 [%s] 登录。该用户已有 %d 个常用登录IP。",
                            user.getUserNo(), currentIpAddress, distinctIpCount);
                    log.warn(newIpRiskMsg); // 新IP且历史IP较多，标记为风险
                    if (alertRemarks.length() > 0) alertRemarks.append(" ");
                    alertRemarks.append(newIpRiskMsg);
                } else {
                    String newIpInfoMsg = String.format("提示：用户 [%s] 从IP地址 [%s] 首次登录。",
                            user.getUserNo(), currentIpAddress);
                    log.info(newIpInfoMsg); // 新IP但历史IP不多，仅作提示
                    if (alertRemarks.length() > 0) alertRemarks.append(" ");
                    alertRemarks.append(newIpInfoMsg);
                }
            } else if (Boolean.FALSE.equals(knownIpEntry.getIsTrusted())) {
                 // IP已知，但如果isTrusted为false，也可以考虑每次都提示或记录，取决于策略
                 // log.info("用户 [{}] 从已知但未信任IP [{}] 登录", user.getUserNo(), currentIpAddress);
            }
        }
        return alertRemarks.toString();
    }

    private HttpServletRequest getCurrentHttpRequest() {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return (sra != null) ? sra.getRequest() : null;
    }

    private String getClientIpAddress(HttpServletRequest request) {
        String xForwardedForHeader = request.getHeader("X-Forwarded-For");
        if (xForwardedForHeader != null && !xForwardedForHeader.isEmpty()) {
            return xForwardedForHeader.split(",")[0].trim();
        }
        return request.getRemoteAddr();
    }
} 